package com.uuah.server.util;

import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;

import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.InputSource;

import com.uuah.api.util.IStreamCloser;
import com.uuah.server.api.IUuahContext;
import com.uuah.server.api.util.IStreamCloserService;

/**
 * Closes streams of different types by looking up available
 * {@link IStreamCloser}'s from the Uuah registry.
 *
 * <p>
 * update record:updator，update time，update content and version
 * </p>
 *
 * @author <a href="jonny_quan@hotmail.com">jonny</a>
 * @date 2009 2009-3-27 下午03:21:16
 * @version 1.0.0
 */
public class StreamCloserService implements IStreamCloserService {

	private static final Logger log = LoggerFactory
			.getLogger(StreamCloserService.class);

	private IUuahContext uuahContext;
	private IStreamCloser coreStreamTypesCloser = new CoreStreamTypesCloser();

	public void closeStream(Object stream) {
		try {
			if (coreStreamTypesCloser.canClose(stream.getClass())) {
				coreStreamTypesCloser.close(stream);
			} else {
				Iterator closers = uuahContext.getRegistry().lookupObjects(
						IStreamCloser.class).iterator();
				while (closers.hasNext()) {
					IStreamCloser closer = (IStreamCloser) closers.next();
					if (closer.canClose(stream.getClass())) {
						closer.close(stream);
					} else {
						log
								.debug("Unable to find an StreamCloser for the stream type: "
										+ stream.getClass()
										+ ", the stream: "
										+ stream + " will not been closed.");
					}
				}
			}
		} catch (Exception e) {
			log.debug("Exception closing stream: " + stream, e);
		}

	}

	public void setUuahContext(IUuahContext context) {
		uuahContext = context;
	}

	static class CoreStreamTypesCloser implements IStreamCloser {

		public boolean canClose(Class streamType) {
			return InputStream.class.isAssignableFrom(streamType)
					|| InputSource.class.isAssignableFrom(streamType)
					|| StreamSource.class.isAssignableFrom(streamType)
					|| (SAXSource.class.isAssignableFrom(streamType) && !streamType
							.getName().endsWith("StaxSource"));
		}

		public void close(Object stream) throws IOException {
			if (stream instanceof InputStream) {
				try {
					((InputStream) stream).close();
				} catch (IOException e) {
					// no-op
				}
			} else if (stream instanceof InputSource) {
				closeInputSourceStream((InputSource) stream);
			} else if (stream instanceof SAXSource) {
				closeInputSourceStream(((SAXSource) stream).getInputSource());
			} else if (stream instanceof StreamSource) {
				try {
					((StreamSource) stream).getInputStream().close();
				} catch (IOException e) {
				}
			}
		}

		private void closeInputSourceStream(InputSource payload)
				throws IOException {
			if (payload.getByteStream() != null) {
				payload.getByteStream().close();
			} else if (payload.getCharacterStream() != null) {
				payload.getCharacterStream().close();
			}
		}

	}

}
